home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / mus / play / multiplsr.lha / main.c < prev    next >
C/C++ Source or Header  |  1992-09-14  |  40KB  |  1,596 lines

  1. /*
  2.  * MultiPlayer
  3.  * Copyright (C) 1992 Bryan Ford
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  * See "Player.doc" for information on contacting the author.
  20.  *
  21.  * $Id: main.c,v 5.1 92/09/14 18:42:11 BAF Exp $
  22.  *
  23.  * $Log:    main.c,v $
  24.  * Revision 5.1  92/09/14  18:42:11  BAF
  25.  * Fixed progload() to deal with NULL filedir's
  26.  * Added nasty_audio support in prefschanged()
  27.  * 
  28.  * Revision 4.7  92/08/21  06:18:09  BAF
  29.  * Put initrexx() back in
  30.  *
  31.  * Revision 4.6  92/07/19  22:16:22  BAF
  32.  * Calling format for progload() and progsave() changed
  33.  * Fixed nasty progwin hanging bug
  34.  *
  35.  * Revision 4.5  92/07/19  18:10:20  BAF
  36.  * Adjustment for putting localdata back into RemindNodes
  37.  * Added support for separate cont/play gadgets on main window (added gui_play())
  38.  *
  39.  * Revision 4.4  92/07/12  08:25:22  BAF
  40.  * shutdown() can no longer fail
  41.  *
  42.  * Revision 4.3  92/07/11  11:20:19  BAF
  43.  * Protracker tempo commands now *really* default to on
  44.  * DiskObject now parsed even if it's read from CLI mode
  45.  *
  46.  * Revision 4.2  92/06/21  11:12:46  BAF
  47.  * Migrated regargs to stdargs
  48.  *
  49.  * Revision 4.1  92/06/06  19:56:18  BAF
  50.  * Major_code_cleanup
  51.  *
  52.  * Revision 3.3  92/05/25  08:22:04  BAF
  53.  * Forgot to bump version number to 1.30
  54.  *
  55.  * Revision 3.2  92/05/25  07:53:17  BAF
  56.  * GNU-ized.
  57.  *
  58.  */
  59.  
  60.  
  61. #define VERSIONTEXT "1.32"
  62. #define REVISIONTEXT "5"
  63.  
  64. #include <exec/types.h>
  65. #include <exec/interrupts.h>
  66. #include <exec/execbase.h>
  67. #include <exec/memory.h>
  68. #include <devices/inputevent.h>
  69. #include <hardware/intbits.h>
  70. #include <dos/dos.h>
  71. #include <dos/dosextens.h>
  72. #include <intuition/intuitionbase.h>
  73. #include <workbench/startup.h>
  74. #include <workbench/workbench.h>
  75. #include <workbench/icon.h>
  76. #include <libraries/commodities.h>
  77. #include <proto/exec.h>
  78. #include <proto/dos.h>
  79. #include <proto/graphics.h>
  80. #include <proto/commodities.h>
  81. #include <proto/icon.h>
  82. #include <proto/wb.h>
  83. #include <proto/diskfont.h>
  84. #include <string.h>
  85. #include <stdlib.h>
  86.  
  87. #include "bry/macros.h"
  88. #include "bry/bovs.h"
  89. #include "bry/remind.h"
  90. #include "bry/misc.h"
  91. #include "guido.h"
  92.  
  93. #include "player.h"
  94.  
  95. /* This text is displayed in the screen titlebar of every MultiPlayer window.  */
  96. char copyright[] = "MultiPlayer " VERSIONTEXT " - Copyright 1992 Bryan Ford";
  97.  
  98.  
  99. /* Startup info for Bovs */
  100. char *progname = "MultiPlayer";
  101. long stack = 4096;
  102. long priority = 1;
  103.  
  104. char *argtemplate = "DIR=DIRECTORY,PLAY=MODULES/M,PROG=PROGRAM/K,SCREEN/K,NOREQUEST/S,NOWINDOW/S,NOREXX/S,NODETACH/S";
  105. char *argexthelp =
  106.   "Usage: Player [[DIRECTORY] dir [modules ...]]|[PLAY modules ...]|[PROG program]\n"
  107.   "              [SCREEN pubscreen] [NOREQUEST] [NOWINDOW] [NOREXX] [NODETACH]\n"
  108.   "\n"
  109.   "  dir [modules ...]     Plays [modules] which must be in directory [dir].\n"
  110.   "  PLAY modules ...      Plays [modules] which must be in current directory.\n"
  111.   "  PROG program          Loads [program] without playing anything immediately.\n"
  112.   "  SCREEN pubscreen      Opens MultiPlayer on a public screen named [pubscreen].\n"
  113.   "  NOREQUEST             Disables opening the file requester on startup.\n"
  114.   "  NOWINDOW              Opens MultiPlayer in the background, without any window.\n"
  115.   "  NOREXX                Disables the ARexx port.\n"
  116.   "  NODETACH              Causes MultiPlayer to run in the original CLI window.\n";
  117. struct PlayerArgs argarray;
  118.  
  119. /* System base pointers */
  120. extern struct ExecBase *SysBase;
  121. struct GfxBase *GfxBase;
  122. struct IntuitionBase *IntuitionBase;
  123. struct Library *GadToolsBase;
  124. struct Library *WorkbenchBase;
  125. struct Library *IconBase;
  126. struct Library *AslBase;
  127. struct Library *ArpBase;
  128. struct Library *XpkBase;
  129. struct Library *CxBase;
  130. struct Library *DiskfontBase;
  131. struct Resource *BattMemBase;
  132.  
  133.  
  134. /* Pointer to our own (main) process */
  135. struct Process *procpt;
  136.  
  137.  
  138. /* This is here for the version command to pick up.  */
  139. char versiontag[] = "$VER: MultiPlayer " VERSIONTEXT;
  140.  
  141.  
  142. /* These contain simple system state flags; the values are defined in
  143.    player.h and player.i.  */
  144. char sysflags, sysflags2;
  145.  
  146. struct MsgPort *wbappport;
  147.  
  148.  
  149. /* The TextAttrs hold the current font specs for the user interface fonts set
  150.    in prefs.  The TextFont pointers are used only to lock the specified fonts
  151.    in memory - they are not actually passed to Guido or the operating system
  152.    directly.  (If the fonts were not locked, memory crunches would expunge
  153.    them and you'd start seeing some very strange results next time something
  154.    is redrawn...)  */
  155. struct TextAttr mainattr, listattr;
  156. static struct TextFont *mainfont, *listfont;
  157.  
  158.  
  159. /* Various RemindLists used throughout the user interface system */
  160.  
  161. /* The dolist is destructively called (destructively means using
  162.    remind_callrem()) after any event.  It is normally used by external
  163.    modules to "remember" operations that must be done soon, but not yet.  For
  164.    example, closing windows is handled this way: in general, closing a window
  165.    in the basic handling routine is not a good idea, because that window may
  166.    be accessed in previous call levels.  Instead, the action is put on the
  167.    dolist, and the window is only closed when we get clear back to the main
  168.    loop, which is considered the only truly 'safe' territory for such
  169.    state changes to occur in.  Nodes on the DoList must return zero.
  170.  
  171.    Besides being called from the main loop, the dolist is called in the
  172.    shutdown routine at an appropriate point, so if you add something to the
  173.    dolist, it is *guaranteed* to be called before the system exits.
  174.    */
  175. struct RemindList dolist;
  176.  
  177.  
  178. /* The checklist is called non-destructively after any event
  179.    (signal).  The global parameter is the mask of signals received from the
  180.    last Wait() call.  RemindNodes use this value to see if their signal was
  181.    activated, and if not, return immediately to avoid wasting time
  182.    performing useless checks.  Nodes must return zero.  */
  183. struct RemindList checklist;
  184.  
  185.  
  186. /* The updatelist holds a list of routines that want to be called when the
  187.    SF_WINDOWUP flag is set in sysflags.  Nodes must always return 0.  This
  188.    is a rather ugly hack: windows and such need to do their own private
  189.    updating.  As such, this RemindList is being phased out.  */
  190. struct RemindList updatelist;
  191.  
  192.  
  193. /* The timerlist is called whenever the SF_SECOND flag gets set, usually
  194.    by the vblank interrupt.  It is used mainly to update the clock on the
  195.    main control panel.  This should not be used by new code: the whole
  196.    timer system badly needs rewriting.  */
  197. struct RemindList timerlist;
  198.  
  199.  
  200. /* The snaplist is called by the save() function in prefswin.c, to snapshot
  201.    current window positions (and sizes, if necessary) before saving the
  202.    preferences file.  All nodes must return 0.  */
  203. struct RemindList snaplist;
  204.  
  205.  
  206. /* The closelist is called by window.c to close all user interface windows
  207.    (either when terminating MultiPlayer completely, or when simply going
  208.    into the 'hidden' state).  It is also used to remake the user interface
  209.    windows after a font change.  The global parameter is 0 to simply close
  210.    the windows, or 1 to close and try to re-open it.
  211.  
  212.    This list is always called destructively, even for a remake operation.
  213.    If a given node succeeds in re-opening its window, it must manually add
  214.    itself back onto the closelist.  (This is typically done by openmpwin().)
  215.  
  216.    Nodes on this list are expected to close their windows immediately,
  217.    rather than triggering a 'delayed close'.  Calls through this list is
  218.    'defined' as being safe territory for closing windows.  In fact, delayed
  219.    closing is typically implemented by removing a window's closelist node
  220.    and moving it to the dolist.
  221.  
  222.    Nodes must return zero.  */
  223. struct RemindList closelist;
  224.  
  225.  
  226. /* The endlist is called destructively when MultiPlayer is about to terminate.
  227.    It and the dolist are called alternately until both lists are completely
  228.    empty.  Only then will MultiPlayer terminate.  Nodes usually return zero...
  229.  
  230.    Icky hack:  If a node returns a positive number, the termination is
  231.    aborted, a "Can't exit at this point" error message is displayed on the
  232.    window, and life returns to normal.  This is because Commodore, in its
  233.    infinite wisdom, forgot to allow ASL requesters to be closed by the
  234.    program (as well as by the user).  Any node that returns -1 must re-add
  235.    its own endnode to the endlist before returning, if it wants to continue
  236.    to be called.  */
  237. struct RemindList endlist;
  238.  
  239.  
  240. /* Commodities-Exchange objects */
  241. static struct MsgPort *brokerport;
  242. static CxObj *broker, *cxfilter, *cxsender, *cxtranslate;
  243.  
  244.  
  245. /* This always contains the icon we started from, if available.  It is
  246.    used to find ToolTypes, as well as to provide imagery for MultiPlayer's
  247.    AppIcons.  */
  248. static struct DiskObject *dob;
  249.  
  250.  
  251. /* Currently activated AppIcons:  Dropping stuff into appi is like dropping
  252.    it into the main window; dropping stuff into appia is like dropping stuff
  253.    into the program window.  */
  254. static struct AppIcon *appi, *appia;
  255.  
  256.  
  257. #define errgoto(mes,lab) { err = mes; goto lab; }
  258.  
  259.  
  260. /* This function is called from all over; it displays the given error message
  261.    (if non-null) in the window if the window is open, otherwise it ignores
  262.    it.  It always returns what it was called with.  */
  263. char *
  264. showerr(char *mes)
  265. {
  266.   extern void windowerror(char *mes);
  267.  
  268.   if(mes)
  269.     windowerror(mes);
  270.   return(mes);
  271. }
  272.  
  273.  
  274. /* This variable always contains the "official" current directory (default
  275.    directory for modules and such).  If nonzero, it must be freed at
  276.    some point.  */
  277. static BPTR dirlock;
  278.  
  279. static void
  280. unlockcurdir(void)
  281. {
  282.   UnLock(dirlock);
  283.   dirlock = 0L;
  284. }
  285.  
  286. char *
  287. setdir(BPTR newdir)
  288. {
  289.   BPTR dir;
  290.  
  291.   if(((dir = newdir) == 0) || (dir = DupLock(newdir)))
  292.     {
  293.       UnLock(dirlock);
  294.       CurrentDir(dirlock = dir);
  295.       return(0L);
  296.     }
  297.   else
  298.     return("Directory not found");
  299. }
  300.  
  301. char *
  302. setdirname(char *name)
  303. {
  304.   BPTR dir;
  305.  
  306.   if(!(name) || !(*name))
  307.     return(0L);
  308.   if(dir = Lock(name,ACCESS_READ))
  309.     {
  310.       UnLock(dirlock);
  311.       CurrentDir(dirlock = dir);
  312.       return(0L);
  313.     }
  314.   else
  315.     return("Directory not found");
  316. }
  317.  
  318. /* 1.3 kludges */
  319.  
  320. #pragma libcall ArpBase CompareLock 1C8 1002
  321. ULONG CompareLock (BPTR, BPTR);
  322.  
  323. int
  324. samelock(BPTR a,BPTR b)
  325. {
  326.   struct FileLock *aa, *bb;
  327.  
  328.   if(SysBase->LibNode.lib_Version >= 36)
  329.     return(SameLock(a,b) == LOCK_SAME);
  330.   if(ArpBase)
  331.     return(CompareLock(a,b) == 0);
  332.   aa = BADDR(a);
  333.   bb = BADDR(b);
  334.   return((aa) && (bb) && (aa->fl_Task == bb->fl_Task) && (aa->fl_Key == bb->fl_Key));
  335. }
  336.  
  337. #pragma libcall ArpBase PathName 14A 18003
  338. int PathName (BPTR, char *,LONG);
  339.  
  340. int
  341. namefromlock(BPTR lock,char *buf,int len)
  342. {
  343.   if(SysBase->LibNode.lib_Version >= 36)
  344.     return(NameFromLock(lock,buf,len));
  345.   if(ArpBase)
  346.     return(PathName(lock,buf,len));
  347.   return(0);
  348. }
  349.  
  350. /* Programs */
  351.  
  352. static void att(void) {GlobPostMod(&modlist,0);}
  353. static void det(void) {GlobPreMod(&modlist,0);}
  354.  
  355. void
  356. programcurmodchanged(void)
  357. {
  358.   void programsettingschanged(void);
  359.   extern short volume, balance, speed;
  360.   extern char modflags, filter;
  361.   extern long songendtime;
  362.  
  363.   if(curmod)
  364.     {
  365.       GlobSetLong(&volume,
  366.         ((unsigned long)curmod->volume<<16)+(unsigned long)((unsigned short)curmod->balance),
  367.         programsettingschanged);
  368.       GlobSetWord(&speed,curmod->speed,programsettingschanged);
  369.       GlobSetByte(&modflags,curmod->flags,programsettingschanged);
  370.       GlobSetByte(&filter,curmod->filter,programsettingschanged);
  371.       songendtime = curmod->endtime;
  372.     }
  373.   else
  374.     {
  375.       GlobSetLong(&volume,-1L,programsettingschanged);
  376.       GlobSetWord(&speed,0,programsettingschanged);
  377.       GlobSetByte(&modflags,0,programsettingschanged);
  378.       GlobSetByte(&filter,(char)-1,programsettingschanged);
  379.       songendtime = 0;
  380.     }
  381. }
  382.  
  383. void
  384. programsettingschanged(void)
  385. {
  386. void programcurmodchanged(void);
  387. extern short volume, balance, speed;
  388. extern char modflags, filter;
  389. short mod;
  390.  
  391.   if(!curmod)
  392.     return;
  393.   if(curmod->volume != volume)
  394.     mod = 1,curmod->volume = volume;
  395.   if(curmod->balance != balance)
  396.     mod = 1,curmod->balance = balance;
  397.   if(curmod->speed != speed)
  398.     mod = 1,curmod->speed = speed;
  399.   if(curmod->flags != modflags)
  400.     mod = 1,curmod->flags = modflags;
  401.   if(curmod->filter != filter)
  402.     mod = 1,curmod->filter = filter;
  403.   if(mod)
  404.     GlobPostMod(&curmod,programcurmodchanged);
  405. }
  406.  
  407. static void
  408. freemodnode(struct ModNode *node)
  409. {
  410.   UnLock(node->dir);
  411.   FreeMem(node,node->size);
  412. }
  413.  
  414. static struct ModNode *
  415. progminadd(BPTR dir,char *name)
  416. {
  417.   struct ModNode *node;
  418.   int size = sizeof(*node)+strlen(name);
  419.  
  420.   if(!(node = AllocMem(size,MEMF_CLEAR)))
  421.     return(0L);
  422.   strcpy(node->name,name);
  423.   node->nameptr = toupperlong(((long*)(node->name))[0]) == 'MOD.' ?
  424.     node->name+4 : node->name;
  425.   node->dir = DupLock(dir ? dir : procpt->pr_CurrentDir);
  426.   node->size = size;
  427.  
  428.   node->volume = 100; /* Pull some default settings out of the hat */
  429.   node->speed = 50;
  430.   node->flags = MNF_PTTEMPO;
  431.  
  432.   AddTail(&modlist,node);
  433.   return(node);
  434. }
  435.  
  436. static char *
  437. progquickadd(BPTR dir,char *name)
  438. {
  439. struct ModNode *node;
  440.  
  441.   if(!(node = progminadd(dir,name)))
  442.     return("Not enough memory");
  443.  
  444.   if(curmod) /* Use the current module's settings if possible */
  445.     {
  446.       if(curmod->volume >= 0)
  447.         node->volume = curmod->volume,
  448.         node->balance = curmod->balance;
  449.       if(curmod->speed)
  450.         node->speed = curmod->speed;
  451.       if(curmod->filter >= 0)
  452.         node->filter = curmod->filter;
  453.       node->flags = MNF_PTTEMPO;
  454.     }
  455.  
  456.   return(0L);
  457. }
  458.  
  459. char *
  460. progadd(BPTR dir,char *name)
  461. {
  462. struct FileInfoBlock __aligned FIB;
  463. BPTR olddir = 0L, lock = 0L;
  464. char *err = 0L;
  465. char flag = 1;
  466. char chdir = 0;
  467.  
  468.   /* Collect all modules in subdirectories */
  469.   if((name) && (name[0]))
  470.     {
  471.       if(dir)
  472.         olddir = CurrentDir(dir), chdir = 1;
  473.       lock = Lock(name,ACCESS_READ);
  474.     }
  475.   else
  476.     lock = DupLock(dir);
  477.   if((lock) && (Examine(lock,&FIB)) && (FIB.fib_DirEntryType > 0))
  478.     {
  479.       flag = 0;
  480.       while(ExNext(lock,&FIB))
  481.         {
  482.           if(strcmp(".info",FIB.fib_FileName+strlen(FIB.fib_FileName)-5) == 0)
  483.             continue;
  484.           if(err = progadd(lock,FIB.fib_FileName))
  485.             break;
  486.         }
  487.     }
  488.   UnLock(lock);
  489.   if(chdir)
  490.     CurrentDir(olddir);
  491.  
  492.   /* Not a directory - just add to the list */
  493.   if((flag) && (!err))
  494.     {
  495.       det();
  496.       err = progquickadd(dir,name);
  497.       att();
  498.     }
  499.   return(err);
  500. }
  501.  
  502. char *
  503. progaddargs(int args,struct WBArg *arg)
  504. {
  505. char *err;
  506.  
  507.   while(args-- > 0)
  508.     {
  509.       if(err = progadd(arg->wa_Lock,arg->wa_Name))
  510.         return(err);
  511.       arg++;
  512.     }
  513.   return(0L);
  514. }
  515.  
  516. char *
  517. progact(struct ModNode *node)
  518. {
  519. char *err;
  520.  
  521.   GlobSetLong(&curmod,node,0L);
  522.   if((err = setdir(node->dir)) ||
  523.     (err = startmod(node->name,-1)))
  524.     return(err);
  525.   return(0L);
  526. }
  527.  
  528. static char *
  529. progfind(struct ModNode *node,int backwards)
  530. {
  531. struct ModNode *onode;
  532. short cnt;
  533. long randomlong(void);
  534.  
  535.   if(!modlist.mlh_Head->mln_Succ) /* No modules */
  536.     return("No modules in program");
  537.   if(!modlist.mlh_Head->mln_Succ->mln_Succ) /* Only one module */
  538.     {
  539.       dofirst:
  540.       return(progact(modlist.mlh_Head));
  541.     }
  542.   if(!node)
  543.     {
  544.       if(playmode == PM_RANDOM)
  545.         node = modlist.mlh_Head;
  546.       else
  547.         goto dofirst;
  548.     }
  549.   onode = node;
  550.   tryagain:
  551.   cnt = playmode == PM_RANDOM ? randomlong() & 0xf : 0;
  552.   do
  553.     {
  554.       if(!backwards)
  555.         {
  556.           node = node->node.mln_Succ;
  557.           if(!node->node.mln_Succ)
  558.             node = modlist.mlh_Head;
  559.         }
  560.       else
  561.         {
  562.           node = node->node.mln_Pred;
  563.           if(!node->node.mln_Pred)
  564.             node = modlist.mlh_TailPred;
  565.         }
  566.     }
  567.   while(--cnt >= 0);
  568.   if(node == onode)
  569.     goto tryagain;
  570.   return(progact(node));
  571. }
  572.  
  573. char *
  574. progstart(void)
  575. {
  576.   return(progfind(0L,0));
  577. }
  578.  
  579. char *
  580. prognext(void)
  581. {
  582.   return(progfind(curmod,0));
  583. }
  584.  
  585. char *
  586. progprev(void)
  587. {
  588.   return(progfind(curmod,1));
  589. }
  590.  
  591. void
  592. progdel(struct ModNode *node)
  593. {
  594.   det();
  595.   if(curmod == node)
  596.     GlobSetLong(&curmod,0,0);
  597.   if(selmod == node)
  598.     GlobSetLong(&selmod,0,0);
  599.   Remove(node);
  600.   freemodnode(node);
  601.   att();
  602. }
  603.  
  604. void
  605. progclear(void)
  606. {
  607. struct ModNode *node;
  608.  
  609.   det();
  610.   while(node = RemTail(&modlist))
  611.     freemodnode(node);
  612.   GlobSetLong(&curmod,0,0);
  613.   GlobSetLong(&selmod,0,0);
  614.   att();
  615. }
  616.  
  617. static char *
  618. writeval2(char *b,unsigned long n)
  619. {
  620.   if(n >= 10)
  621.     b = writeval2(b,n/10), n%=10;
  622.   *b = '0'+n;
  623.   return(b+1);
  624. }
  625.  
  626. static char *
  627. writeval(char *b,char i,long n)
  628. {
  629.   *b++ = i;
  630.   if(n<0)
  631.     *b++ = '-', n = -n;
  632.   return(writeval2(b,n));
  633. }
  634.  
  635. char *
  636. progsave(BPTR filedir,char *filename)
  637. {
  638.   static char hdr[] = "PROGRAM\nDIR ";
  639.   BPTR han, dir = 0L;
  640.   struct ModNode *node = modlist.mlh_Head;
  641.   char *err = "Error saving program";
  642.   char buf[32], *b;
  643.   BPTR olddir;
  644.  
  645.   if(!node->node.mln_Succ)
  646.     return("Nothing to save");
  647.   olddir = CurrentDir(filedir);
  648.   han = Open(filename,MODE_NEWFILE);
  649.   CurrentDir(olddir);
  650.   if(!han)
  651.     goto err1;
  652.   if(Write(han,&hdr[0],7) <= 0)
  653.     goto err2;
  654.   while(node->node.mln_Succ)
  655.     {
  656.       if(!samelock(dir,node->dir))
  657.         {
  658.         char buf[256];
  659.  
  660.           dir = node->dir;
  661.           if(!namefromlock(dir,buf,255))
  662.             errgoto("Can't find directory name",err2);
  663.           if((Write(han,&hdr[7],5) <= 0) ||
  664.             (Write(han,buf,strlen(buf)) <= 0))
  665.             goto err2;
  666.         }
  667.       if((Write(han,&hdr[7],1) <= 0) ||
  668.         (Write(han,node->name,strlen(node->name)) <= 0))
  669.         goto err2;
  670.  
  671.       b = buf;
  672.       *b++ = '\n';
  673.       *b++ = '\t';
  674.       if(node->volume != 100)
  675.         b = writeval(b,'V',node->volume);
  676.       if(node->volume >= 0)
  677.         {
  678.           if(node->balance)
  679.             b = writeval(b,'B',node->balance);
  680.           if(node->flags & MNF_ENDFADE)
  681.             *b++ = 'A';
  682.         }
  683.       if(node->speed != 50)
  684.         b = writeval(b,'S',node->speed);
  685.       if(node->filter)
  686.         b = writeval(b,'F',node->filter);
  687.       if(node->endtime)
  688.         b = writeval(b,'E',node->endtime);
  689.       if(node->flags & MNF_NOTPROTRACKER)
  690.         *b++ = 'N';
  691.       else if(node->flags & MNF_PTTEMPO)
  692.         *b++ = 'T';
  693.       if(b != &buf[2])
  694.         {
  695.           if(Write(han,buf,(long)b-(long)buf) <= 0)
  696.             goto err2;
  697.         }
  698.  
  699.       node = node->node.mln_Succ;
  700.     }
  701.   err = 0L;
  702.   err2:
  703.   Close(han);
  704.   err1:
  705.   return(err);
  706. }
  707.  
  708. char *
  709. progload(BPTR filedir,char *filename,int start)
  710. {
  711.   static char hdr[] = "PROGRAM\nDIR ";
  712.   BPTR han, dir = 0L;
  713.   char *mem, *p, *ep;
  714.   long size, v;
  715.   char *err = "Error reading program";
  716.   struct ModNode *node = 0L;
  717.   BPTR olddir;
  718.  
  719.   progclear();
  720.   if(filedir)
  721.     {
  722.       olddir = CurrentDir(filedir);
  723.       han = Open(filename,MODE_OLDFILE);
  724.       CurrentDir(olddir);
  725.     }
  726.   else
  727.     han = Open(filename,MODE_OLDFILE);
  728.   if(!han)
  729.     goto err1;
  730.   if((Seek(han,0,OFFSET_END) < 0) ||
  731.     ((size = Seek(han,0,OFFSET_BEGINNING)) < 0))
  732.     goto err2;
  733.   size += 1;
  734.   if(!(mem = AllocMem(size,0)))
  735.     errgoto("Not enough memory",err2);
  736.   if(Read(han,mem,size-1) <= 0)
  737.     goto err3;
  738.   if((size <= 8) || memcmp(mem,&hdr[0],8))
  739.     errgoto("Not a program file",err3);
  740.   ep = mem+size-1;
  741.   for(p = mem+8;p < ep;p++)
  742.     if(*p == '\n')
  743.       *p = 0;
  744.   *p = 0;
  745.   p = mem+8;
  746.   det();
  747.   while(p < ep)
  748.     {
  749.       if((*p) && (*p != '#') && (*p != '*') && (*p != '/'))
  750.         {
  751.           if(p[0] == '\t')
  752.             {
  753.               if(node)
  754.                 {
  755.                   p++;
  756.                   node->flags = 0;
  757.                   while(*p != 0)
  758.                     {
  759.                       switch(*p++)
  760.                         {
  761.                           case 'V':
  762.                             v = atol(p);
  763.                             if((v <= 100) && (v >= -1))
  764.                               node->volume = v;
  765.                             break;
  766.                           case 'B':
  767.                             v = atol(p);
  768.                             if((v <= 50) && (v >= -50))
  769.                               node->balance = v;
  770.                             break;
  771.                           case 'S':
  772.                             v = atol(p);
  773.                             if((v == 0) || ((v <= 200) && (v >= 10)))
  774.                               node->speed = v;
  775.                             break;
  776.                           case 'F':
  777.                             v = atol(p);
  778.                             if((v <= 2) && (v >= -1))
  779.                               node->filter = v;
  780.                             break;
  781.                           case 'E':
  782.                             v = atol(p);
  783.                             if(v > 0)
  784.                               node->endtime = v;
  785.                             break;
  786.                           case 'A':
  787.                             node->flags |= MNF_ENDFADE;
  788.                             break;
  789.                           case 'T':
  790.                             node->flags |= MNF_PTTEMPO;
  791.                             break;
  792.                           case 'N':
  793.                             node->flags |= MNF_NOTPROTRACKER;
  794.                             break;
  795.                         }
  796.                     }
  797.                 }
  798.             }
  799.           else if(memcmp(p,&hdr[8],4) == 0)
  800.             {
  801.               p += 4;
  802.               UnLock(dir);
  803.               if(!(dir = Lock(p,ACCESS_READ)))
  804.                 errgoto("Directory not found",err3);
  805.             }
  806.           else
  807.             {
  808.               if(!(node = progminadd(dir,p)))
  809.                 errgoto("Not enough memory",err4);
  810.             }
  811.         }
  812.       p += strlen(p)+1;
  813.     }
  814.   err = start ? progstart() : 0L;
  815.   err4:
  816.   att();
  817.   UnLock(dir);
  818.   err3:
  819.   FreeMem(mem,size);
  820.   err2:
  821.   Close(han);
  822.   err1:
  823.   return(err);
  824. }
  825.  
  826. void
  827. addcalls(struct RemindNode *check,
  828.          struct RemindNode *update,
  829.          struct RemindNode *timer,
  830.          struct RemindNode *end)
  831. {
  832.   if(check)
  833.     remind_add(&checklist,check);
  834.   if(update)
  835.     remind_add(&updatelist,update);
  836.   if(timer)
  837.     remind_add(&timerlist,timer);
  838.   if(end)
  839.     remind_add(&endlist,end);
  840. }
  841.  
  842.  
  843. static long sigmask = SIGBREAKF_CTRL_E|SIGBREAKF_CTRL_F;
  844.  
  845. void
  846. addsigs(long newsigs)
  847. {
  848.   sigmask |= newsigs;
  849. }
  850.  
  851. void
  852. remsigs(long oldsigs)
  853. {
  854.   sigmask &= ~oldsigs;
  855. }
  856.  
  857.  
  858. void
  859. gui_play(void)
  860. {
  861.   showerr(playmod(-1));
  862. }
  863.  
  864.  
  865. char *
  866. fadecont(void)
  867. {
  868.   extern short fadevol, fadeinc;
  869.   extern char pausefade, playing;
  870.   char *err;
  871.  
  872.   if(pausefade
  873.      && ((!playing) || (sysflags & SF_FADEPAUSE))
  874.      && (curmod)
  875.      && (curmod->volume >= 0))
  876.     {
  877.       sysflags &= ~SF_FADEPAUSE;
  878.       fadeinc = 1;
  879.       if(fadevol <= 0)
  880.         fadevol = 1;
  881.       if(err = contplaymod())
  882.         fadevol = -1;
  883.     }
  884.   else
  885.     err = contplaymod();
  886.   return(err);
  887. }
  888.  
  889.  
  890. void
  891. gui_cont(void)
  892. {
  893.   showerr(fadecont());
  894. }
  895.  
  896.  
  897.  
  898. void
  899. fadestop(void)
  900. {
  901.   extern short fadevol, fadeinc;
  902.   extern char pausefade, playing;
  903.  
  904.   if(pausefade && playing && curmod && (curmod->volume >= 0))
  905.     {
  906.       sysflags |= SF_FADEPAUSE;
  907.       fadeinc = -1;
  908.       if(fadevol < 0)
  909.         fadevol = 128;
  910.     }
  911.   else
  912.     stopmod();
  913. }
  914.  
  915. void
  916. gui_stop(void)
  917. {
  918.   fadestop();
  919. }
  920.  
  921.  
  922. void
  923. windowkey(int code,int context)
  924. {
  925.   extern void gui_windowzoom(void);
  926.   extern char *gui_progwinopenclose(void);
  927.   extern char *gui_prefswinopenclose(void);
  928.   extern char *gui_settingswinopenclose(void);
  929.   extern char *gui_infowinopenclose(void);
  930.   extern char *gui_flashywinopenclose(void);
  931.  
  932.   extern long cursong, numsongs;
  933.   extern char playing;
  934.   extern char prefsglob[];
  935.  
  936.   char *err = 0L;
  937.  
  938.   switch(code)
  939.     {
  940.       case RawESC:
  941.         sysflags |= SF_KILL;
  942.         break;
  943.       case RawF1:
  944.         gui_windowzoom();
  945.         break;
  946.       case RawF2:
  947.         gui_progwinopenclose();
  948.         break;
  949.       case RawF3:
  950.         gui_prefswinopenclose();
  951.         break;
  952.       case RawF4:
  953.         gui_settingswinopenclose();
  954.         break;
  955.       case RawF5:
  956.         gui_infowinopenclose();
  957.         break;
  958.       case RawF6:                     /* Silly, ain't it? ... */
  959.         flashflags ^= FF_SPECTRUM|FF_NOTES;
  960.       case RawF7:
  961.         flashflags ^= FF_NOTES|FF_SCOPE;
  962.       case RawF8:
  963.         flashflags ^= FF_SCOPE|FF_QSCOPE;
  964.       case RawF9:
  965.         flashflags ^= FF_QSCOPE;
  966.         GlobPostMod(prefsglob,0);
  967.         break;
  968.       case RawSpace:
  969.         if((playing) && !(sysflags & SF_FADEPAUSE))
  970.           fadestop();
  971.         else
  972.           fadecont();
  973.         break;
  974.       case RawDel:
  975.         endmod();
  976.         break;
  977.       case RawDown:
  978.         err = prognext();
  979.         break;
  980.       case RawUp:
  981.         err = progprev();
  982.         break;
  983.       case RawRight:
  984.         setsong(cursong+1);
  985.         break;
  986.       case RawLeft:
  987.         setsong(cursong > 0 ? cursong-1 : numsongs-1);
  988.         break;
  989.     }
  990.   showerr(err);
  991. }
  992.  
  993.  
  994. void
  995. standardcallback(struct GuidoMessage *im)
  996. {
  997.   if(im->Class == IDCMP_RAWKEY)
  998.     windowkey(im->Code,PREFSWINCONTEXT);
  999.   else if(im->Class == IDCMP_MOUSEBUTTONS)
  1000.     windowclick(im->Code);
  1001. }
  1002.  
  1003.  
  1004. static void
  1005. closefonts(void)
  1006. {
  1007.   if(mainfont)
  1008.     CloseFont(mainfont), mainfont = 0;
  1009.   if(listfont)
  1010.     CloseFont(listfont), listfont = 0;
  1011. }
  1012.  
  1013.  
  1014. static struct TextFont *
  1015. openfont(struct TextAttr *attr,char *name,short size)
  1016. {
  1017.   attr->ta_Name = name;
  1018.   attr->ta_YSize = size;
  1019.   if(DiskfontBase)
  1020.     return(OpenDiskFont(attr));
  1021.   return(OpenFont(attr));
  1022. }
  1023.  
  1024. void
  1025. mainprefschanged(void)
  1026. {
  1027.   { /* Set our hotkey string */
  1028.   extern char hotkey[];
  1029.   IX ix;
  1030.  
  1031.     if(!cxfilter)
  1032.       return;
  1033.     RemoveCxObj(cxfilter);
  1034.     if(hotkey[0])
  1035.       {
  1036.         ix.ix_Version = IX_VERSION;
  1037.         if(ParseIX(hotkey,&ix) >= 0)
  1038.           {
  1039.             SetFilterIX(cxfilter,&ix);
  1040.             AttachCxObj(broker,cxfilter);
  1041.           }
  1042.         else
  1043.           showerr("Can't decipher hot key string");
  1044.       }
  1045.   }
  1046.  
  1047.   { /* Add or remove our AppIcon */
  1048.   extern char appicon, appicona;
  1049.  
  1050.     if(appicon && !appi && wbappport && dob)
  1051.       appi = AddAppIconA(1,0,"Play",wbappport,0,dob,0);
  1052.     else if(!appicon && appi)
  1053.       RemoveAppIcon(appi), appi = 0;
  1054.  
  1055.     if(appicona && !appia && wbappport && dob)
  1056.       appia = AddAppIconA(0,0,"Add",wbappport,0,dob,0);
  1057.     else if(!appicona && appia)
  1058.       RemoveAppIcon(appia), appia = 0;
  1059.   }
  1060.  
  1061.   { /* The changing of the fonts */
  1062.   extern char mainfontname[], listfontname[];
  1063.   extern short mainfontsize, listfontsize;
  1064.  
  1065.     if(!mainfont || !listfont ||
  1066.       (mainfontsize != mainfont->tf_YSize) || (listfontsize != listfont->tf_YSize) ||
  1067.       stricmp(mainfontname,mainfont->tf_Message.mn_Node.ln_Name) ||
  1068.       stricmp(mainfontname,mainfont->tf_Message.mn_Node.ln_Name))
  1069.       {
  1070.       int reset = 0;
  1071.  
  1072.         closefonts();
  1073.         while(!(mainfont = openfont(&mainattr,mainfontname,mainfontsize)))
  1074.           strcpy(mainfontname,"topaz.font"), mainfontsize = 8, reset = 1;
  1075.         while(!(listfont = openfont(&listattr,listfontname,listfontsize)))
  1076.           strcpy(listfontname,"topaz.font"), listfontsize = 8, reset = 1;
  1077.         windowremake();
  1078.         if(reset)
  1079.           GlobPostMod(prefsglob,mainprefschanged);
  1080.       }
  1081.   }
  1082.  
  1083.   { /* Set the audio to nasty or nice */
  1084.     extern char nasty_audio;
  1085.     extern void NoteSysAudioPri(char pri);
  1086.  
  1087.     NoteSysAudioPri(nasty_audio ? 127 : 0);
  1088.   }
  1089. }
  1090.  
  1091.  
  1092. static void
  1093. loadprefs(void)
  1094. {
  1095.   extern char prefs[], prefsend[], prefsglob[];
  1096.   BPTR han;
  1097.  
  1098.   if(han = Open("ENV:MultiPlayerPrefs",MODE_OLDFILE))
  1099.     {
  1100.       Read(han,prefs,(long)(prefsend-prefs));
  1101.       Close(han);
  1102.       if(playmode >= ' ')
  1103.         switch(toupper(playmode))
  1104.           {
  1105.             case 'S': playmode = PM_SEQUENCE; break;
  1106.             case 'R': playmode = PM_RANDOM; break;
  1107.             case 'O': playmode = PM_ONCE; break;
  1108.             case 'V': playmode = PM_OVEROVER; break;
  1109.           }
  1110.       if(showmode >= ' ')
  1111.         switch(toupper(showmode))
  1112.           {
  1113.             case 'N': showmode = SM_NOTHING; break;
  1114.             case 'C': showmode = SM_CLOCK; break;
  1115.             case '0': showmode = SM_SEQUENCE0; break;
  1116.             case '1': showmode = SM_SEQUENCE1; break;
  1117.           }
  1118.     }
  1119.   GlobPostMod(prefsglob,0);
  1120. }
  1121.  
  1122. static void
  1123. checkwbapp(void)
  1124. {
  1125.   struct AppMessage *msg;
  1126.  
  1127.   if(!wbappport)
  1128.     return;
  1129.   while(msg = GetMsg(wbappport))
  1130.     {
  1131.     int start = 0;
  1132.  
  1133.       if(msg->am_NumArgs)
  1134.         {
  1135.           if(msg->am_ID)
  1136.             progclear();
  1137.           if(!showerr(progaddargs(msg->am_NumArgs,msg->am_ArgList)) && (msg->am_ID))
  1138.             start = 1;
  1139.         }
  1140.       else
  1141.         showerr(windowopen());
  1142.       ReplyMsg(msg);
  1143.       if(start)
  1144.         showerr(progstart());
  1145.     }
  1146. }
  1147.  
  1148. static void
  1149. checkbroker(void)
  1150. {
  1151.   CxMsg *msg;
  1152.  
  1153.   if(!brokerport)
  1154.     return;
  1155.   while(msg = GetMsg(brokerport))
  1156.     {
  1157.       switch(CxMsgType(msg))
  1158.         {
  1159.           case CXM_COMMAND:
  1160.             switch(CxMsgID(msg))
  1161.               {
  1162.                 case CXCMD_DISABLE:
  1163.                   ActivateCxObj(broker,0);
  1164.                   break;
  1165.                 case CXCMD_ENABLE:
  1166.                   ActivateCxObj(broker,1);
  1167.                   break;
  1168.                 case CXCMD_KILL:
  1169.                   sysflags |= SF_KILL;
  1170.                   break;
  1171.                 case CXCMD_DISAPPEAR:
  1172.                   windowclose();
  1173.                   break;
  1174.                 case CXCMD_UNIQUE:
  1175.                   ActivateCxObj(broker,1);
  1176.                 case CXCMD_APPEAR:
  1177.                   goto appear;
  1178.               }
  1179.             break;
  1180.           case CXM_IEVENT:
  1181.           appear:
  1182.             showerr(windowopen());
  1183.             break;
  1184.         }
  1185.       ReplyMsg(msg);
  1186.     }
  1187. }
  1188.  
  1189. static long
  1190. standardsnapshot(int dummy,struct MPWin *win)
  1191. {
  1192.   if(win->win)
  1193.     {
  1194.       win->snappos[0] = win->win->LeftEdge;
  1195.       win->snappos[1] = win->win->TopEdge;
  1196.     }
  1197.   return(0);
  1198. }
  1199.  
  1200. void
  1201. closempwin(struct MPWin *win)
  1202. {
  1203.   remind_rem(&win->snapnode);
  1204.   remind_rem(&win->checknode);
  1205.   remind_rem(&win->closenode);
  1206.   if(win->win)
  1207.     {
  1208.       if(win->snapnode.callfunc)
  1209.         (*win->snapnode.callfunc)(0,win);
  1210.       remsigs(win->sigmask);
  1211.       GClosePanel(win->win);
  1212.       win->sigmask = win->win = 0L;
  1213.     }
  1214. }
  1215.  
  1216. static long
  1217. standardclose(int remake,struct MPWin *win)
  1218. {
  1219.   closempwin(win);
  1220.   if(remake)
  1221.     showerr(openmpwin(win));
  1222.   return(0);
  1223. }
  1224.  
  1225. char *
  1226. openmpwin(struct MPWin *win)
  1227. {
  1228.   win->snapnode.localdata = win->checknode.localdata = win->closenode.localdata = win;
  1229.   if(win->snappos && !win->snapnode.callfunc)
  1230.     win->snapnode.callfunc = standardsnapshot;
  1231.   if(win->snapnode.callfunc)
  1232.     remind_add(&snaplist,&win->snapnode);
  1233.   if(win->checknode.callfunc)
  1234.     remind_add(&checklist,&win->checknode);
  1235.   if(!win->closenode.callfunc)
  1236.     win->closenode.callfunc = standardclose;
  1237.   remind_rem(&win->closenode); /* In case it was on the dolist */
  1238.   remind_add(&closelist,&win->closenode);
  1239.  
  1240.   if(win->win = GOpenPanel(win->spec,0,&mainattr))
  1241.     {
  1242.       addsigs(win->sigmask = 1<<win->win->UserPort->mp_SigBit);
  1243.       if(win->initfunc)
  1244.         (*(win->initfunc))();
  1245.       return(0L);
  1246.     }
  1247.   else
  1248.     {
  1249.       closempwin(win);
  1250.       return("Unable to open window");
  1251.     }
  1252. }
  1253.  
  1254. void setfinmpwin(struct MPWin *win)
  1255.   {
  1256.     remind_rem(&win->closenode);
  1257.     remind_add(&dolist,&win->closenode);
  1258.   }
  1259.  
  1260. /* Program cleanup routine, called only by Main().  */
  1261. static void
  1262. shutdown(void)
  1263. {
  1264.   extern struct Interrupt vblankint;
  1265.   extern void unloadftm(void);
  1266.   long rc;
  1267.  
  1268.   endmod();
  1269.  
  1270.   while(((rc = remind_callrem(&dolist,0L)) >= 0)
  1271.         || ((rc = remind_callrem(&endlist,0L)) >= 0));
  1272.  
  1273.   unloadftm();  /* FIXME - kludge - yuck */
  1274.  
  1275.   progclear();
  1276.   unlockcurdir();
  1277.  
  1278.   if(broker)
  1279.     {
  1280.       ActivateCxObj(broker,0);
  1281.       if(cxfilter)
  1282.         DeleteCxObj(cxfilter);
  1283.       if(cxtranslate)
  1284.         DeleteCxObj(cxtranslate);
  1285.       if(cxsender)
  1286.         DeleteCxObj(cxsender);
  1287.       DeleteCxObj(broker);
  1288.     }
  1289.   if(brokerport)
  1290.     DeleteMsgPort(brokerport);
  1291.  
  1292.   if(appi)
  1293.     RemoveAppIcon(appi);
  1294.   if(appia)
  1295.     RemoveAppIcon(appia);
  1296.   checkwbapp();
  1297.   if(wbappport)
  1298.     DeleteMsgPort(wbappport);
  1299.  
  1300.   if(dob)
  1301.     FreeDiskObject(dob);
  1302.  
  1303.   RemIntServer(INTB_VERTB,&vblankint);
  1304.  
  1305.   closefonts();
  1306.  
  1307.   if(DiskfontBase)
  1308.     CloseLibrary(DiskfontBase);
  1309.   if(CxBase)
  1310.     CloseLibrary(CxBase);
  1311.   if(XpkBase)
  1312.     CloseLibrary(XpkBase);
  1313.   if(ArpBase)
  1314.     CloseLibrary(ArpBase);
  1315.   if(AslBase)
  1316.     CloseLibrary(AslBase);
  1317.   if(IconBase)
  1318.     CloseLibrary(IconBase);
  1319.   if(WorkbenchBase)
  1320.     CloseLibrary(WorkbenchBase);
  1321.   if(GadToolsBase)
  1322.     CloseLibrary(GadToolsBase);
  1323.   CloseLibrary(IntuitionBase);
  1324.   CloseLibrary(GfxBase);
  1325. }
  1326.  
  1327. /* Initialize-everything routine, called only by Main() */
  1328. static void
  1329. startup(int arglen,char *argstr)
  1330. {
  1331.   static struct NewBroker nb =
  1332.     {
  1333.       NB_VERSION,
  1334.       "MultiPlayer",
  1335.       "MultiPlayer",
  1336.       "MultiPlayer " VERSIONTEXT " - © 1992 Bryan Ford",
  1337.       NBU_UNIQUE|NBU_NOTIFY,COF_SHOW_HIDE,0
  1338.     };
  1339.  
  1340.   extern struct Interrupt vblankint;
  1341.   extern long scopeinterval;
  1342.   extern void initrexx(void);
  1343.   extern void randomseedclock(void);
  1344.   extern char keepprog;
  1345.   extern int argsparsed;
  1346.  
  1347.   struct WBStartup *wbs = argstr;
  1348.  
  1349.   remind_initlist(&dolist);
  1350.   remind_initlist(&checklist);
  1351.   remind_initlist(&updatelist);
  1352.   remind_initlist(&timerlist);
  1353.   remind_initlist(&snaplist);
  1354.   remind_initlist(&closelist);
  1355.   remind_initlist(&endlist);
  1356.  
  1357.   procpt = FindTask(0L);
  1358.   GfxBase = OpenLibrary("graphics.library",0);
  1359.   IntuitionBase = OpenLibrary("intuition.library",0);
  1360.   GadToolsBase = OpenLibrary("gadtools.library",0);
  1361.   WorkbenchBase = OpenLibrary("workbench.library",0);
  1362.   IconBase = OpenLibrary("icon.library",0);
  1363.   AslBase = OpenLibrary("asl.library",37);
  1364.   ArpBase = OpenLibrary("arp.library",39);
  1365.   if(!(XpkBase = OpenLibrary("xpkmaster.library",0)))
  1366.     XpkBase = OpenLibrary("compression.library",0);
  1367.   CxBase = OpenLibrary("commodities.library",0);
  1368.   DiskfontBase = OpenLibrary("diskfont.library",0);
  1369.   BattMemBase = OpenResource("ciab.resource");
  1370.  
  1371.   AddIntServer(INTB_VERTB,&vblankint);
  1372.  
  1373.   randomseedclock();
  1374.  
  1375.   if(WorkbenchBase)
  1376.     wbappport = CreateMsgPort(),
  1377.     addsigs(1<<wbappport->mp_SigBit);
  1378.  
  1379.   scopeinterval = (long)(FFperF*CLOCK)/(long)SysBase->VBlankFrequency;
  1380.  
  1381.   if(IconBase)
  1382.     {
  1383.       char *p;
  1384.  
  1385.       if(!arglen)
  1386.         {
  1387.         BPTR olddir = 0;
  1388.  
  1389.           if(wbs->sm_ArgList[0].wa_Lock)
  1390.             olddir = CurrentDir(wbs->sm_ArgList[0].wa_Lock);
  1391.           if(wbs->sm_ArgList[0].wa_Name)
  1392.             dob = GetDiskObject(wbs->sm_ArgList[0].wa_Name);
  1393.           if(olddir)
  1394.             CurrentDir(olddir);
  1395.         }
  1396.       else
  1397.         dob = GetDiskObject("Player");
  1398.       if(dob)
  1399.         {
  1400.           if((p = FindToolType(dob->do_ToolTypes,"DIR")) ||
  1401.             (p = FindToolType(dob->do_ToolTypes,"DIRECTORY")))
  1402.             argarray.dir = p;
  1403.           if((p = FindToolType(dob->do_ToolTypes,"PROG")) ||
  1404.             (p = FindToolType(dob->do_ToolTypes,"PROGRAM")))
  1405.             argarray.prog = p;
  1406.           if(p = FindToolType(dob->do_ToolTypes,"SCREEN"))
  1407.             argarray.screen = p;
  1408.           if(p = FindToolType(dob->do_ToolTypes,"REQUEST"))
  1409.             argarray.noreq = MatchToolValue(p,"NO");
  1410.           if(p = FindToolType(dob->do_ToolTypes,"WINDOW"))
  1411.             argarray.nowin = MatchToolValue(p,"NO");
  1412.           if(p = FindToolType(dob->do_ToolTypes,"REXX"))
  1413.             argarray.norexx = MatchToolValue(p,"NO");
  1414.         }
  1415.     }
  1416.  
  1417.   if(CxBase)
  1418.     {
  1419.       if(brokerport = nb.nb_Port = CreateMsgPort())
  1420.         {
  1421.           addsigs(1<<brokerport->mp_SigBit);
  1422.           if(broker = CxBroker(&nb,0))
  1423.             {
  1424.               if((cxsender = CxSender(brokerport,0)) &&
  1425.                 (cxtranslate = CxTranslate(IECLASS_NULL)) &&
  1426.                 (cxfilter = CxFilter(0)))
  1427.                 {
  1428.                   AttachCxObj(cxfilter,cxsender);
  1429.                   AttachCxObj(cxfilter,cxtranslate);
  1430.                   /* Don't attach the filter to the broker: prefs does that */
  1431.                 }
  1432.             }
  1433.           else /* Don't allow duplicates */
  1434.             {
  1435.               shutdown();
  1436.               BRExit(10,ERROR_OBJECT_EXISTS);
  1437.             }
  1438.         }
  1439.     }
  1440.  
  1441.   loadprefs();
  1442.  
  1443.   /* Now that we have our hot-key (if any), activate it */
  1444.   if(broker)
  1445.     ActivateCxObj(broker,1);
  1446.  
  1447.   if(!argarray.nowin)
  1448.     windowopen();
  1449.  
  1450.   if(!argarray.norexx)
  1451.     initrexx();
  1452.  
  1453.   if(arglen) /* CLI */
  1454.     {
  1455.       if(argsparsed) /* 2.0 arguments */
  1456.         {
  1457.         char **mods;
  1458.  
  1459.           if(argarray.dir)
  1460.             showerr(setdirname(argarray.dir));
  1461.           if(mods = argarray.mods)
  1462.             {
  1463.               while(*mods)
  1464.                 {
  1465.                   if(showerr(progadd(0L,*mods)))
  1466.                     break;
  1467.                   mods++;
  1468.                 }
  1469.               argarray.noreq = 1;
  1470.             }
  1471.         }
  1472.       else /* 1.3 arguments */
  1473.         {
  1474.           arglen--;
  1475.           while(argstr[0] == ' ')
  1476.             argstr++, arglen--;
  1477.           while((arglen > 0) && (argstr[arglen-1] == ' '))
  1478.             arglen--;
  1479.           if(arglen > 0)
  1480.             {
  1481.               argstr[arglen--] = 0;
  1482.               showerr((argstr[arglen] == ':') || (argstr[arglen] == '/') ?
  1483.                 setdirname(argstr) :
  1484.                 (argarray.noreq = 1, progadd(0L,argstr)));
  1485.             }
  1486.         }
  1487.     }
  1488.   else /* Workbench */
  1489.     {
  1490.       showerr(wbs->sm_NumArgs > 1 ?
  1491.         (argarray.noreq = 1, progaddargs(wbs->sm_NumArgs-1,wbs->sm_ArgList+1)) :
  1492.         argarray.dir ? setdirname(argarray.dir) : setdir(wbs->sm_ArgList[0].wa_Lock));
  1493.     }
  1494.  
  1495.   if(argarray.noreq)
  1496.     {
  1497.       if(modlist.mlh_Head->mln_Succ)
  1498.         showerr(progstart());
  1499.     }
  1500.   else if(argarray.prog)
  1501.     {
  1502.       showerr(progload(procpt->pr_CurrentDir,argarray.prog,0));
  1503.     }
  1504.   else if(keepprog)
  1505.     {
  1506.       showerr(progload(0,"ENV:MultiPlayerProgram",0));
  1507.     }
  1508.   else
  1509.     showerr(reqmod(1));
  1510. }
  1511.  
  1512. /* Main entrypoint and program loop - called from Bovs if detach enabled,
  1513.    called from PreStart() if detach disabled. */
  1514. void
  1515. Main(int arglen,char *argstr)
  1516. {
  1517.   extern long songtime, songendtime;
  1518.   extern short fadevol, volume;
  1519.   long gotsigs;
  1520.  
  1521.   startup(arglen,argstr);
  1522.  
  1523.   do
  1524.     {
  1525.       gotsigs = Wait(sigmask);
  1526.  
  1527.       checkwbapp();
  1528.       checkbroker();
  1529.       remind_call(&checklist,gotsigs);
  1530.       remind_callrem(&dolist,0L);
  1531.       if(sysflags & SF_TIMERUP)
  1532.         {
  1533.         extern void windowupdatetimer(void);
  1534.  
  1535.           windowupdatetimer();
  1536.         }
  1537.       if(sysflags & SF_SECOND)
  1538.         {
  1539.           sysflags &= ~SF_SECOND;
  1540.           remind_call(&timerlist,0);
  1541.         }
  1542.       if(sysflags & SF_WINDOWUP)
  1543.         {
  1544.           sysflags &= ~SF_WINDOWUP;
  1545.           remind_call(&updatelist,0);
  1546.         }
  1547.       if(sysflags & SF_REPEAT)
  1548.         {
  1549.           if(fadevol <= 0)
  1550.             {
  1551.             extern char contprogonerr;
  1552.  
  1553.               if((playmode == PM_ONCE)
  1554.                  || ((playmode == PM_SEQUENCEONCE) && (curmod)
  1555.                      && !(curmod->node.mln_Succ->mln_Succ)))
  1556.                 endmod();
  1557.               else if(playmode != PM_OVEROVER)
  1558.                 {
  1559.                   /* If an error occurs and "continue on error" is
  1560.                      selected, retry ONLY ONCE - otherwise it's very
  1561.                      easy to get in infinite loops (by clearing the
  1562.                      program, for example). */
  1563.                   if(showerr(prognext()) && contprogonerr)
  1564.                     prognext();
  1565.                 }
  1566.               fadevol = -1;
  1567.               GlobPostMod(&volume,programsettingschanged);
  1568.               sysflags &= ~(SF_REPEAT|SF_FADEDONE);
  1569.             }
  1570.         }
  1571.       if(sysflags & SF_FADEDONE)
  1572.         {
  1573.           if(sysflags & SF_FADEPAUSE)
  1574.             stopmod();
  1575.           fadevol = -1;
  1576.           GlobPostMod(&volume,programsettingschanged);
  1577.           sysflags &= ~(SF_FADEDONE|SF_FADEPAUSE);
  1578.         }
  1579.     }
  1580.   while(!(sysflags & SF_KILL));
  1581.   shutdown();
  1582. }
  1583.  
  1584. /* PreStart() is called by Bovs before detaching, and if the user specifies
  1585.    NODETACH, it simply calls Main() immediately without ever detaching.  */
  1586. void
  1587. PreStart (int arglen, char *argstr)
  1588. {
  1589.   if(argarray.nodetach)
  1590.     {
  1591.       Main(arglen,argstr);
  1592.       BExit();
  1593.     }
  1594. }
  1595.  
  1596.